Businesses like banks which provide service have to worry about problem of 'Customer Churn' i.e. customers leaving and joining another service provider. It is important to understand which aspects of the service influence a customer's decision in this regard. Management can concentrate efforts on improvement of service, keeping in mind these priorities.
You as a Data scientist with the bank need to build a neural network based classifier that can determine whether a customer will leave the bank or not in the next 6 months.
CustomerId: Unique ID which is assigned to each customer
Surname: Last name of the customer
CreditScore: It defines the credit history of the customer.
Geography: A customer’s location
Gender: It defines the Gender of the customer
Age: Age of the customer
Tenure: Number of years for which the customer has been with the bank
NumOfProducts: refers to the number of products that a customer has purchased through the bank.
Balance: Account balance
HasCrCard: It is a categorical variable which decides whether the customer has credit card or not.
EstimatedSalary: Estimated salary
isActiveMember: Is is a categorical variable which decides whether the customer is active member of the bank or not ( Active member in the sense, using bank products regularly, making transactions etc )
Exited : whether or not the customer left the bank within six month. It can take two values 0=No ( Customer did not leave the bank ) 1=Yes ( Customer left the bank )
# Installing the libraries with the specified version.
# !pip install tensorflow==2.15.0 scikit-learn==1.2.2 seaborn==0.13.1 matplotlib==3.7.1 numpy==1.25.2 pandas==2.0.3 imbalanced-learn==0.10.1 -q --user
# Libraries to help with reading and manipulating data
import pandas as pd
import numpy as np
import time
# libaries to help with data visualization
import matplotlib.pyplot as plt
import seaborn as sns
# Library to split data
from sklearn.model_selection import train_test_split
# library to import to standardize the data
from sklearn.preprocessing import StandardScaler, LabelEncoder
# importing different functions to build models
import tensorflow as tf
from tensorflow import keras
from keras import backend
from keras.models import Sequential
from keras.layers import Dense, Dropout
from tensorflow.keras.layers import BatchNormalization
# importing SMOTE
from imblearn.over_sampling import SMOTE
# importing metrics
from sklearn.metrics import confusion_matrix,roc_curve,classification_report,recall_score
import random
# Library to avoid the warnings
import warnings
warnings.filterwarnings("ignore")
from google.colab import drive
drive.mount('/content/drive')
churn = pd.read_csv('/content/drive/MyDrive/Python Course/Churn.csv')
# Make a copy of the data
ds = churn.copy()
# let's view the first 5 rows of the data
ds.head()
# let's view the last 5 rows of the data
ds.tail()
# Checking the number of rows and columns in the training data
ds.shape
ds.info()
ds.describe()
# let's check for missing values in the data
ds.isna().sum()
ds.duplicated().sum()
ds.nunique()
# Drop RowNumber, CustomerId and Surname
ds = ds.drop(['RowNumber', 'CustomerId', 'Surname'], axis=1)
for i in ds.describe(include=["object"]).columns:
print("Unique values in", i, "are :")
print(ds[i].value_counts())
print("*" * 50)
# function to plot a boxplot and a histogram along the same scale.
def histogram_boxplot(data, feature, figsize=(12, 7), kde=False, bins=None):
"""
Boxplot and histogram combined
data: dataframe
feature: dataframe column
figsize: size of figure (default (12,7))
kde: whether to show the density curve (default False)
bins: number of bins for histogram (default None)
"""
f2, (ax_box2, ax_hist2) = plt.subplots(
nrows=2, # Number of rows of the subplot grid= 2
sharex=True, # x-axis will be shared among all subplots
gridspec_kw={"height_ratios": (0.25, 0.75)},
figsize=figsize,
) # creating the 2 subplots
sns.boxplot(
data=data, x=feature, ax=ax_box2, showmeans=True, color="violet"
) # boxplot will be created and a star will indicate the mean value of the column
sns.histplot(
data=data, x=feature, kde=kde, ax=ax_hist2, bins=bins, palette="winter"
) if bins else sns.histplot(
data=data, x=feature, kde=kde, ax=ax_hist2
) # For histogram
ax_hist2.axvline(
data[feature].mean(), color="green", linestyle="--"
) # Add mean to the histogram
ax_hist2.axvline(
data[feature].median(), color="black", linestyle="-"
) # Add median to the histogram
# function to create labeled barplots
def labeled_barplot(data, feature, perc=False, n=None):
"""
Barplot with percentage at the top
data: dataframe
feature: dataframe column
perc: whether to display percentages instead of count (default is False)
n: displays the top n category levels (default is None, i.e., display all levels)
"""
total = len(data[feature]) # length of the column
count = data[feature].nunique()
if n is None:
plt.figure(figsize=(count + 1, 5))
else:
plt.figure(figsize=(n + 1, 5))
plt.xticks(rotation=90, fontsize=15)
ax = sns.countplot(
data=data,
x=feature,
palette="Paired",
order=data[feature].value_counts().index[:n].sort_values(),
)
for p in ax.patches:
if perc == True:
label = "{:.1f}%".format(
100 * p.get_height() / total
) # percentage of each class of the category
else:
label = p.get_height() # count of each level of the category
x = p.get_x() + p.get_width() / 2 # width of the plot
y = p.get_height() # height of the plot
ax.annotate(
label,
(x, y),
ha="center",
va="center",
size=12,
xytext=(0, 5),
textcoords="offset points",
) # annotate the percentage
plt.show() # show the plot
histogram_boxplot(ds,'CreditScore', kde=True)
histogram_boxplot(ds, 'Age', kde=True)
histogram_boxplot(ds, 'Balance', kde=True)
histogram_boxplot(ds, 'EstimatedSalary', kde=True)
labeled_barplot(ds, "Exited", perc=True)
labeled_barplot(ds, 'Geography', perc=True)
labeled_barplot(ds, 'Gender', perc=True)
labeled_barplot(ds, 'Tenure', perc=True)
labeled_barplot(ds, 'NumOfProducts', perc=True)
labeled_barplot(ds, 'HasCrCard', perc=True)
labeled_barplot(ds, 'IsActiveMember', perc=True)
# function to plot stacked bar chart
def stacked_barplot(data, predictor, target):
"""
Print the category counts and plot a stacked bar chart
data: dataframe
predictor: independent variable
target: target variable
"""
count = data[predictor].nunique()
sorter = data[target].value_counts().index[-1]
tab1 = pd.crosstab(data[predictor], data[target], margins=True).sort_values(
by=sorter, ascending=False
)
print(tab1)
print("-" * 120)
tab = pd.crosstab(data[predictor], data[target], normalize="index").sort_values(
by=sorter, ascending=False
)
tab.plot(kind="bar", stacked=True, figsize=(count + 1, 5))
plt.legend(
loc="lower left",
frameon=False,
)
plt.legend(loc="upper left", bbox_to_anchor=(1, 1))
plt.show()
### Function to plot distributions
def distribution_plot_wrt_target(data, predictor, target):
fig, axs = plt.subplots(2, 2, figsize=(12, 10))
target_uniq = data[target].unique()
target_uniq.sort()
axs[0, 0].set_title("Distribution of target for target=" + str(target_uniq[0]))
sns.histplot(
data=data[data[target] == target_uniq[0]],
x=predictor,
kde=True,
ax=axs[0, 0],
color="teal",
)
axs[0, 1].set_title("Distribution of target for target=" + str(target_uniq[1]))
sns.histplot(
data=data[data[target] == target_uniq[1]],
x=predictor,
kde=True,
ax=axs[0, 1],
color="orange",
)
axs[1, 0].set_title("Boxplot w.r.t target")
sns.boxplot(data=data, x=target, y=predictor, ax=axs[1, 0], palette="gist_rainbow")
axs[1, 1].set_title("Boxplot (without outliers) w.r.t target")
sns.boxplot(
data=data,
x=target,
y=predictor,
ax=axs[1, 1],
showfliers=False,
palette="gist_rainbow",
)
plt.tight_layout()
plt.show()
# defining the list of numerical columns
cols_list = ["CreditScore","Age","Tenure","Balance","EstimatedSalary", "NumOfProducts"]
# Pairplot with hue=Exited
sns.pairplot(ds, vars=cols_list, hue='Exited', diag_kind='kde')
#plt.show()
plt.figure(figsize=(15, 7))
sns.heatmap(ds[cols_list].corr(), annot=True, vmin=-1, vmax=1, fmt=".2f", cmap="Spectral")
plt.show()
stacked_barplot(ds, "Geography", "Exited" )
stacked_barplot(ds, "Gender", "Exited" )
stacked_barplot(ds, "HasCrCard", "Exited" )
stacked_barplot(ds, "IsActiveMember", "Exited" )
distribution_plot_wrt_target(ds, "CreditScore", "Exited")
distribution_plot_wrt_target(ds, "Age", "Exited")
plt.figure(figsize=(5,5))
sns.boxplot(y='Tenure',x='Exited',data=ds)
plt.show()
distribution_plot_wrt_target(ds, "Balance", "Exited")
plt.figure(figsize=(5,5))
sns.boxplot(y='NumOfProducts',x='Exited',data=ds)
plt.show()
distribution_plot_wrt_target(ds, "EstimatedSalary", "Exited")
ds = pd.get_dummies(ds,columns=ds.select_dtypes(include=["object"]).columns.tolist(),drop_first=True)
ds = ds.astype(float)
ds.head()
# set up the independent and dependent variables
X = ds.drop(['Exited'],axis=1)
y = ds['Exited'] # Exited
# Splitting the dataset into the Training and Testing set.
X_large, X_test, y_large, y_test = train_test_split(X, y, test_size = 0.15, random_state = 42,stratify=y,shuffle = True)
# Splitting the dataset into the Training and Validation set.
X_train, X_val, y_train, y_val = train_test_split(X_large, y_large, test_size = 0.17647, random_state = 42,stratify=y_large, shuffle = True)
print(X_train.shape, X_val.shape, X_test.shape)
print(y_train.shape, y_val.shape, y_test.shape)
Since all the numerical values are on a different scales, bring all of them to the same scale by scaling.
# creating an instance of the standard scaler
sc = StandardScaler()
X_train[cols_list] = sc.fit_transform(X_train[cols_list])
X_val[cols_list] = sc.transform(X_val[cols_list])
X_test[cols_list] = sc.transform(X_test[cols_list])
Let's create a function for plotting the confusion matrix
def make_confusion_matrix(actual_targets, predicted_targets):
"""
To plot the confusion_matrix with percentages
actual_targets: actual target (dependent) variable values
predicted_targets: predicted target (dependent) variable values
"""
cm = confusion_matrix(actual_targets, predicted_targets)
labels = np.asarray(
[
["{0:0.0f}".format(item) + "\n{0:.2%}".format(item / cm.flatten().sum())]
for item in cm.flatten()
]
).reshape(cm.shape[0], cm.shape[1])
plt.figure(figsize=(6, 4))
sns.heatmap(cm, annot=labels, fmt="")
plt.ylabel("True label")
plt.xlabel("Predicted label")
Create the plot function for repetitive use
def plot(history, name):
"""
Function to plot loss/accuracy
history: an object which stores the metrics and losses.
name: can be one of Loss or Accuracy
"""
fig, ax = plt.subplots() #Creating a subplot with figure and axes.
plt.plot(history.history[name]) #Plotting the train accuracy or train loss
plt.plot(history.history['val_'+name]) #Plotting the validation accuracy or validation loss
plt.title('Model ' + name.capitalize()) #Defining the title of the plot.
plt.ylabel(name.capitalize()) #Capitalizing the first letter.
plt.xlabel('Epoch') #Defining the label for the x-axis.
fig.legend(['Train', 'Validation'], loc="outside right upper") #Defining the legend, loc controls the position of the legend.
Let's create two blank dataframes that will store the recall values for all the models we build.
train_metric_df = pd.DataFrame(columns=["recall"])
valid_metric_df = pd.DataFrame(columns=["recall"])
Create a more comprehensive results dataframe
columns = ["# hidden layers","# neurons - hidden layer","activation function - hidden layer ","# epochs","batch size","optimizer","time(secs)","Train_loss","Valid_loss","Train_Recall","Valid_Recall"]
results = pd.DataFrame(columns=columns)
# Clear the session memory
backend.clear_session()
# Fixing the seed for random number generators so that we can ensure we receive the same output everytime
np.random.seed(2)
random.seed(2)
tf.random.set_seed(2)
# Initializing the neural network
model_nhl = Sequential()
# Adding the input layer with neurons
model_nhl.add(Dense(1, activation = 'sigmoid', input_dim = X_train.shape[1]))
# Use SGD as the optimizer.
optimizer = tf.keras.optimizers.SGD(0.001)
# uncomment one of the following lines to define the metric to be used
# metric = 'accuracy'
metric = keras.metrics.Recall()
# metric = keras.metrics.Precision()
# metric = keras.metrics.F1Score()
## Complete the code to compile the model with binary cross entropy as loss function and recall as the metric.
model_nhl.compile(loss='binary_crossentropy',optimizer=optimizer,metrics=[metric])
model_nhl.summary()
epochs = 50
batch_size = X_train.shape[0]
# Get start time
start = time.time()
# Fitting the ANN
history_nhl = model_nhl.fit(
X_train, y_train,
batch_size=batch_size,
validation_data=(X_val,y_val),
epochs=epochs,
verbose=1
)
# Get end time
end=time.time()
# Time taken
print("Time taken in seconds ",end-start)
model_nhl.evaluate(X_train,y_train)
model_nhl.evaluate(X_val,y_val)
Loss function
# Plotting Train Loss vs Validation Loss
plot(history_nhl, 'loss')
Recall
# Plotting Train recall vs Validation recall
plot(history_nhl, 'recall')
# Predicting the results using best as a threshold
y_train_pred = model_nhl.predict(X_train)
y_train_pred = (y_train_pred > 0.5)
y_train_pred
# Predicting the results using best as a threshold
y_val_pred = model_nhl.predict(X_val)
y_val_pred = (y_val_pred > 0.5)
y_val_pred
model_name = "NN with SGD with No Hidden Layers"
train_metric_df.loc[model_name] = recall_score(y_train, y_train_pred)
valid_metric_df.loc[model_name] = recall_score(y_val, y_val_pred)
results.loc[model_name]=['-','-','-',epochs,batch_size,'SGD',(end-start),history_nhl.history["loss"][-1],history_nhl.history["val_loss"][-1],history_nhl.history["recall"][-1],history_nhl.history["val_recall"][-1]]
results
Classification report
# Classification report
cr = classification_report(y_train, y_train_pred)
print(cr)
# Classification report
cr = classification_report(y_val, y_val_pred)
print(cr)
Confusion matrix
make_confusion_matrix(y_train, y_train_pred)
make_confusion_matrix(y_val, y_val_pred)
# Clear the session memory
backend.clear_session()
# Fixing the seed for random number generators so that we can ensure we receive the same output everytime
np.random.seed(2)
random.seed(2)
tf.random.set_seed(2)
# Initializing the neural network
model_0 = Sequential()
# Adding the input layer with neurons and relu as the activation function
model_0.add(Dense(14, activation='relu', input_dim = X_train.shape[1]))
# Adding the output layer with 1 neuron for binary output and using sigmoid for binary output
model_0.add(Dense(1, activation = 'sigmoid'))
# Use SGD as the optimizer.
optimizer = tf.keras.optimizers.SGD(0.001)
# uncomment one of the following lines to define the metric to be used
# metric = 'accuracy'
metric = keras.metrics.Recall()
# metric = keras.metrics.Precision()
# metric = keras.metrics.F1Score()
## Complete the code to compile the model with binary cross entropy as loss function and recall as the metric.
model_0.compile(loss='binary_crossentropy',optimizer=optimizer,metrics=[metric])
model_0.summary()
epochs = 50
batch_size = X_train.shape[0]
# Get start time
start = time.time()
# Fitting the ANN
history_0 = model_0.fit(
X_train, y_train,
batch_size=batch_size,
validation_data=(X_val,y_val),
epochs=epochs,
verbose=1
)
# Get end time
end=time.time()
# Time taken
print("Time taken in seconds ",end-start)
model_0.evaluate(X_train,y_train)
model_0.evaluate(X_val,y_val)
Loss function
# Plotting Train Loss vs Validation Loss
plot(history_0, 'loss')
Recall
# Plotting Train recall vs Validation recall
plot(history_0, 'recall')
# Predicting the results using best as a threshold
y_train_pred = model_0.predict(X_train)
y_train_pred = (y_train_pred > 0.5)
y_train_pred
# Predicting the results using best as a threshold
y_val_pred = model_0.predict(X_val)
y_val_pred = (y_val_pred > 0.5)
y_val_pred
model_name = "NN with SGD - 1 Hidden Layer"
train_metric_df.loc[model_name] = recall_score(y_train, y_train_pred)
valid_metric_df.loc[model_name] = recall_score(y_val, y_val_pred)
results.loc[model_name]=['1','14','relu',epochs,batch_size,'SGD',(end-start),history_0.history["loss"][-1],history_0.history["val_loss"][-1],history_0.history["recall"][-1],history_0.history["val_recall"][-1]]
results
Classification report
# Classification report
cr = classification_report(y_train, y_train_pred)
print(cr)
# Classification report
cr = classification_report(y_val, y_val_pred)
print(cr)
Confusion matrix
make_confusion_matrix(y_train, y_train_pred)
make_confusion_matrix(y_val, y_val_pred)
# Clear the session memory
backend.clear_session()
# Fixing the seed for random number generators so that we can ensure we receive the same output everytime
np.random.seed(2)
random.seed(2)
tf.random.set_seed(2)
# Initializing the neural network
model_01 = Sequential()
# Adding the input layer with neurons and relu as the activation function
model_01.add(Dense(14, activation='relu', input_dim = X_train.shape[1]))
# Adding one hidden layer with neurons and Relu as the activation function
model_01.add(Dense(7, activation='relu'))
# Adding the output layer with 1 neuron for binary output and using sigmoid for binary output
model_01.add(Dense(1, activation = 'sigmoid'))
# Use SGD as the optimizer.
optimizer = tf.keras.optimizers.SGD(0.001)
# uncomment one of the following lines to define the metric to be used
# metric = 'accuracy'
metric = keras.metrics.Recall()
# metric = keras.metrics.Precision()
# metric = keras.metrics.F1Score()
## Complete the code to compile the model with binary cross entropy as loss function and recall as the metric.
model_01.compile(loss='binary_crossentropy',optimizer=optimizer,metrics=[metric])
model_01.summary()
epochs = 50
batch_size = X_train.shape[0]
# Get start time
start = time.time()
# Fitting the ANN
history_01 = model_01.fit(
X_train, y_train,
batch_size=batch_size,
validation_data=(X_val,y_val),
epochs=epochs,
verbose=1
)
# Get end time
end=time.time()
# Time taken
print("Time taken in seconds ",end-start)
model_01.evaluate(X_train,y_train)
model_01.evaluate(X_val,y_val)
Loss function
# Plotting Train Loss vs Validation Loss
plot(history_01, 'loss')
Recall
# Plotting Train recall vs Validation recall
plot(history_01, 'recall')
# Predicting the results using best as a threshold
y_train_pred = model_01.predict(X_train)
y_train_pred = (y_train_pred > 0.5)
y_train_pred
# Predicting the results using best as a threshold
y_val_pred = model_01.predict(X_val)
y_val_pred = (y_val_pred > 0.5)
y_val_pred
model_name = "NN with SGD - 2 Hidden Layers [14,7]"
train_metric_df.loc[model_name] = recall_score(y_train, y_train_pred)
valid_metric_df.loc[model_name] = recall_score(y_val, y_val_pred)
results.loc[model_name]=['2','14,7','relu,relu',epochs,batch_size,'SGD',(end-start),history_01.history["loss"][-1],history_01.history["val_loss"][-1],history_01.history["recall"][-1],history_01.history["val_recall"][-1]]
results
Classification report
# Classification report
cr = classification_report(y_train, y_train_pred)
print(cr)
# Classification report
cr = classification_report(y_val, y_val_pred)
print(cr)
Confusion matrix
make_confusion_matrix(y_train, y_train_pred)
make_confusion_matrix(y_val, y_val_pred)
# Clear the session memory
backend.clear_session()
# Fixing the seed for random number generators so that we can ensure we receive the same output everytime
np.random.seed(2)
random.seed(2)
tf.random.set_seed(2)
# Initializing the neural network
model_02 = Sequential()
# Adding the input layer with neurons and relu as the activation function
model_02.add(Dense(64, activation='relu', input_dim = X_train.shape[1]))
# Adding one hidden layer with neurons and Relu as the activation function
model_02.add(Dense(32, activation='relu'))
# Adding the output layer with 1 neuron for binary output and using sigmoid for binary output
model_02.add(Dense(1, activation = 'sigmoid'))
# Use SGD as the optimizer.
optimizer = tf.keras.optimizers.SGD(0.001)
# uncomment one of the following lines to define the metric to be used
# metric = 'accuracy'
metric = keras.metrics.Recall()
# metric = keras.metrics.Precision()
# metric = keras.metrics.F1Score()
## Complete the code to compile the model with binary cross entropy as loss function and recall as the metric.
model_02.compile(loss='binary_crossentropy',optimizer=optimizer,metrics=[metric])
model_02.summary()
epochs = 50
batch_size = X_train.shape[0]
# Get start time
start = time.time()
# Fitting the ANN
history_02 = model_02.fit(
X_train, y_train,
batch_size=batch_size,
validation_data=(X_val,y_val),
epochs=epochs,
verbose=1
)
# Get end time
end=time.time()
# Time taken
print("Time taken in seconds ",end-start)
model_02.evaluate(X_train,y_train)
model_02.evaluate(X_val,y_val)
Loss function
# Plotting Train Loss vs Validation Loss
plot(history_02, 'loss')
Recall
# Plotting Train recall vs Validation recall
plot(history_02, 'recall')
# Predicting the results using best as a threshold
y_train_pred = model_02.predict(X_train)
y_train_pred = (y_train_pred > 0.5)
y_train_pred
# Predicting the results using best as a threshold
y_val_pred = model_02.predict(X_val)
y_val_pred = (y_val_pred > 0.5)
y_val_pred
model_name = "NN with SGD - 2 Hidden Layers [64,32]"
train_metric_df.loc[model_name] = recall_score(y_train, y_train_pred)
valid_metric_df.loc[model_name] = recall_score(y_val, y_val_pred)
results.loc[model_name]=['2','64,32','relu,relu',epochs,batch_size,'SGD',(end-start),history_02.history["loss"][-1],history_02.history["val_loss"][-1],history_02.history["recall"][-1],history_02.history["val_recall"][-1]]
results
Classification report
# Classification report
cr = classification_report(y_train, y_train_pred)
print(cr)
# Classification report
cr = classification_report(y_val, y_val_pred)
print(cr)
Confusion matrix
make_confusion_matrix(y_train, y_train_pred)
make_confusion_matrix(y_val, y_val_pred)
# Clear the session memory
backend.clear_session()
# Fixing the seed for random number generators so that we can ensure we receive the same output everytime
np.random.seed(2)
random.seed(2)
tf.random.set_seed(2)
# Initializing the neural network
model_1 = Sequential()
model_1.add(Dense(64,activation='relu',input_dim = X_train.shape[1]))
model_1.add(Dense(32,activation='relu'))
model_1.add(Dense(1, activation = 'sigmoid'))
# Complete the code to use Adam as the optimizer.
optimizer = tf.keras.optimizers.Adam()
# uncomment one of the following lines to define the metric to be used
# metric = 'accuracy'
metric = keras.metrics.Recall()
#metric = keras.metrics.Precision()
# metric = keras.metrics.F1Score()
# Complete the code to compile the model with binary cross entropy as loss function and recall as the metric
model_1.compile(loss='binary_crossentropy',optimizer=optimizer,metrics=[metric])
model_1.summary()
epochs = 50
batch_size = 128
# Get start time
start = time.time()
# Fitting the ANN
history_1 = model_1.fit(
X_train,y_train,
batch_size=batch_size,
validation_data=(X_val,y_val),
epochs=epochs,
verbose=1
)
# Get end time
end=time.time()
# Time taken
print("Time taken in seconds ",end-start)
model_1.evaluate(X_train,y_train)
model_1.evaluate(X_val,y_val)
Loss function
# Plotting Train Loss vs Validation Loss
plot(history_1, 'loss')
Recall
# Plotting Train recall vs Validation recall
plot(history_1, 'recall')
#Predicting the results using 0.5 as the threshold
y_train_pred = model_1.predict(X_train)
y_train_pred = (y_train_pred > 0.5)
y_train_pred
#Predicting the results using 0.5 as the threshold
y_val_pred = model_1.predict(X_val)
y_val_pred = (y_val_pred > 0.5)
y_val_pred
model_name = "NN with Adam - 2 Hidden Layers [64,32]"
train_metric_df.loc[model_name] = recall_score(y_train,y_train_pred)
valid_metric_df.loc[model_name] = recall_score(y_val,y_val_pred)
results.loc[model_name]=['2','64,32','relu,relu',epochs,batch_size,'Adam',(end-start),history_1.history["loss"][-1],history_1.history["val_loss"][-1],history_1.history["recall"][-1],history_1.history["val_recall"][-1]]
results
Classification report
#lassification report
cr=classification_report(y_train,y_train_pred)
print(cr)
#classification report
cr=classification_report(y_val, y_val_pred)
print(cr)
Confusion matrix
#Calculating the confusion matrix
make_confusion_matrix(y_train, y_train_pred)
#Calculating the confusion matrix
make_confusion_matrix(y_val, y_val_pred)
results
# Clear up the session memory
backend.clear_session()
# Fixing the seed for random number generators so that we can ensure we receive the same output everytime
np.random.seed(2)
random.seed(2)
tf.random.set_seed(2)
# Initializing the neural network
model_2 = Sequential()
# Add the input layer with neurons and relu as activation function
model_2.add(Dense(64,activation='relu',input_dim = X_train.shape[1]))
# Add dropout with ratio of 0.2 or any suitable value.
model_2.add(Dropout(0.2))
# Add a hidden layer
model_2.add(Dense(32,activation='tanh'))
# Add dropout with ratio of 0.1 or any suitable value.
model_2.add(Dropout(0.2))
# Add the number of neurons required in the output layer.
model_2.add(Dense(1, activation = 'sigmoid'))
# Complete the code to use Adam as the optimizer.
optimizer = tf.keras.optimizers.Adam()
# uncomment one of the following lines to define the metric to be used
# metric = 'accuracy'
metric = keras.metrics.Recall()
# metric = keras.metrics.Precision()
# metric = keras.metrics.F1Score()
## Complete the code to compile the model with binary cross entropy as loss function and recall as the metric.
model_2.compile(loss='binary_crossentropy',optimizer=optimizer,metrics=[metric])
# Summary of the model
model_2.summary()
epochs = 50
batch_size = 128
# Get start time
start = time.time()
# Fitting the ANN with batch_size = 32 and 100 epochs
history_2 = model_2.fit(
X_train,y_train,
batch_size=batch_size,
epochs=epochs,
verbose=1,
validation_data=(X_val,y_val)
)
# Get end time
end=time.time()
# Time taken
print("Time taken in seconds ",end-start)
Loss function
# Plotting Train Loss vs Validation Loss
plot(history_2, 'loss')
# Plotting Train recall vs Validation recall
plot(history_2, 'recall')
#Predicting the results using best as a threshold
y_train_pred = model_2.predict(X_train)
y_train_pred = (y_train_pred > 0.5)
y_train_pred
#Predicting the results using 0.5 as the threshold.
y_val_pred = model_2.predict(X_val)
y_val_pred = (y_val_pred > 0.5)
y_val_pred
model_name = "NN with Adam & Dropout - 2 Hidden Layers [64,32]"
train_metric_df.loc[model_name] = recall_score(y_train,y_train_pred)
valid_metric_df.loc[model_name] = recall_score(y_val,y_val_pred)
results.loc[model_name]=['2','64,32,','relu,tanh',epochs,batch_size,'Adam',(end-start),history_2.history["loss"][-1],history_2.history["val_loss"][-1],history_2.history["recall"][-1],history_2.history["val_recall"][-1]]
results
Classification report
#classification report
cr=classification_report(y_train,y_train_pred)
print(cr)
#classification report
cr = classification_report(y_val,y_val_pred)
print(cr)
Confusion matrix
#Calculating the confusion matrix
make_confusion_matrix(y_train, y_train_pred)
#Calculating the confusion matrix
make_confusion_matrix(y_val,y_val_pred)
# Clear up the session memory
backend.clear_session()
# Fixing the seed for random number generators so that we can ensure we receive the same output everytime
np.random.seed(2)
random.seed(2)
tf.random.set_seed(2)
# Initializing the neural network
model_21 = Sequential()
# Add the input layer with neurons and relu as activation function
model_21.add(Dense(32,activation='relu',input_dim = X_train.shape[1]))
# Add dropout with ratio of 0.2 or any suitable value.
model_21.add(Dropout(0.2))
# Add a hidden layer
model_21.add(Dense(32,activation='tanh'))
# Add a hidden layer
model_21.add(Dense(32,activation='relu'))
# Add dropout with ratio of 0.1 or any suitable value.
model_21.add(Dropout(0.1))
# Add a hidden layer
model_21.add(Dense(32,activation='relu'))
# Add the number of neurons required in the output layer.
model_21.add(Dense(1, activation = 'sigmoid'))
# Complete the code to use Adam as the optimizer.
optimizer = tf.keras.optimizers.Adam()
# uncomment one of the following lines to define the metric to be used
# metric = 'accuracy'
metric = keras.metrics.Recall()
# metric = keras.metrics.Precision()
# metric = keras.metrics.F1Score()
## Complete the code to compile the model with binary cross entropy as loss function and recall as the metric.
model_21.compile(loss='binary_crossentropy',optimizer=optimizer,metrics=[metric])
# Summary of the model
model_21.summary()
epochs = 50
batch_size = 128
# Get start time
start = time.time()
# Fitting the ANN with batch_size = 32 and 100 epochs
history_21 = model_21.fit(
X_train,y_train,
batch_size=batch_size,
epochs=epochs,
verbose=1,
validation_data=(X_val,y_val)
)
# Get end time
end=time.time()
# Time taken
print("Time taken in seconds ",end-start)
Loss function
# Plotting Train Loss vs Validation Loss
plot(history_21, 'loss')
# Plotting Train recall vs Validation recall
plot(history_21, 'recall')
#Predicting the results using best as a threshold
y_train_pred = model_21.predict(X_train)
y_train_pred = (y_train_pred > 0.5)
y_train_pred
#Predicting the results using 0.5 as the threshold.
y_val_pred = model_21.predict(X_val)
y_val_pred = (y_val_pred > 0.5)
y_val_pred
model_name = "NN with Adam & Dropout - 4 Hidden Layers [32,32,32,32]"
train_metric_df.loc[model_name] = recall_score(y_train,y_train_pred)
valid_metric_df.loc[model_name] = recall_score(y_val,y_val_pred)
results.loc[model_name]=['4','32,32,32,32','relu,tanh,relu,relu',epochs,batch_size,'Adam',(end-start),history_21.history["loss"][-1],history_21.history["val_loss"][-1],history_21.history["recall"][-1],history_21.history["val_recall"][-1]]
results
Classification report
#classification report
cr=classification_report(y_train,y_train_pred)
print(cr)
#classification report
cr = classification_report(y_val,y_val_pred)
print(cr)
Confusion matrix
#Calculating the confusion matrix
make_confusion_matrix(y_train, y_train_pred)
#Calculating the confusion matrix
make_confusion_matrix(y_val,y_val_pred)
Let's try to apply SMOTE to balance this dataset and then again apply hyperparamter tuning accordingly.
sm = SMOTE(random_state=42)
# Complete the code to fit SMOTE on the training data.
X_train_smote, y_train_smote= sm.fit_resample(X_train, y_train)
print('After UpSampling, the shape of train_X: {}'.format(X_train_smote.shape))
print('After UpSampling, the shape of train_y: {} \n'.format(y_train_smote.shape))
y_train_smote.value_counts()
Let's build a model with the balanced dataset
# Clear the session memory
backend.clear_session()
# Fixing the seed for random number generators so that we can ensure we receive the same output everytime
np.random.seed(2)
random.seed(2)
tf.random.set_seed(2)
# Initializing the model
model_3 = Sequential()
# Add a input layer
model_3.add(Dense(14,activation='relu',input_dim = X_train_smote.shape[1]))
# Add a hidden layer
model_3.add(Dense(7,activation='relu'))
# Add the required number of neurons in the output layer with a sigmoid activation function.
model_3.add(Dense(1, activation = 'sigmoid'))
#Complete the code to use SGD as the optimizer.
optimizer = tf.keras.optimizers.SGD(0.001)
# uncomment one of the following lines to define the metric to be used
# metric = 'accuracy'
metric = keras.metrics.Recall()
# metric = keras.metrics.Precision()
# metric = keras.metrics.F1Score()
# Complete the code to compile the model with binary cross entropy as loss function and recall as the metric
model_3.compile(loss='binary_crossentropy',optimizer=optimizer,metrics=[metric])
model_3.summary()
epochs = 25
batch_size = 128
# Get start time
start = time.time()
# Fitting the ANN
history_3 = model_3.fit(
X_train_smote, y_train_smote,
batch_size=batch_size,
epochs=epochs,
verbose=1,
validation_data = (X_val,y_val)
)
# Get end time
end=time.time()
# Time taken
print("Time taken in seconds ",end-start)
model_3.evaluate(X_train_smote,y_train_smote)
model_3.evaluate(X_val,y_val)
Loss function
#Plotting Train Loss vs Validation Loss
plot(history_3, 'loss')
# Plotting Train recall vs Validation recall
plot(history_3, 'recall')
y_train_pred = model_3.predict(X_train_smote)
#Predicting the results using 0.5 as the threshold
y_train_pred = (y_train_pred > 0.5)
y_train_pred
y_val_pred = model_3.predict(X_val)
#Predicting the results using 0.5 as the threshold
y_val_pred = (y_val_pred > 0.5)
y_val_pred
model_name = "NN with SMOTE & SGD - 2 Hidden Layers [14,7]"
train_metric_df.loc[model_name] = recall_score(y_train_smote,y_train_pred)
valid_metric_df.loc[model_name] = recall_score(y_val,y_val_pred)
results.loc[model_name]=['2','14,7','relu,relu',epochs,batch_size,'Adam',(end-start),history_3.history["loss"][-1],history_3.history["val_loss"][-1],history_3.history["recall"][-1],history_3.history["val_recall"][-1]]
results
Classification report
cr=classification_report(y_train_smote,y_train_pred)
print(cr)
cr=classification_report(y_val,y_val_pred)
print(cr)
Confusion matrix
#Calculating the confusion matrix
make_confusion_matrix(y_train_smote, y_train_pred)
#Calculating the confusion matrix
make_confusion_matrix(y_val,y_val_pred)
Let's build a model with the balanced dataset
# Clear the session memmory
backend.clear_session()
# Fixing the seed for random number generators so that we can ensure we receive the same output everytime
np.random.seed(2)
random.seed(2)
tf.random.set_seed(2)
# Initializing the model
model_4 = Sequential()
# Add a input layer
model_4.add(Dense(32,activation='relu',input_dim = X_train_smote.shape[1]))
# Add a hidden layer
model_4.add(Dense(32,activation='relu'))
#Add a hidden layer
model_4.add(Dense(32,activation='relu'))
# Add the required number of neurons in the output layer and a suitable activation function.
model_4.add(Dense(1, activation = 'sigmoid'))
model_4.summary()
#Complete the code to use Adam as the optimizer.
optimizer = tf.keras.optimizers.Adam()
# uncomment one of the following lines to define the metric to be used
# metric = 'accuracy'
metric = keras.metrics.Recall()
# metric = keras.metrics.Precision()
# metric = keras.metrics.F1Score()
# Complete the code to compile the model with binary cross entropy as loss function and recall as the metric
model_4.compile(loss='binary_crossentropy',optimizer=optimizer,metrics=[metric])
model_4.summary()
epochs = 25
batch_size = 128
# Get start time
start = time.time()
# Fitting the ANN
history_4 = model_4.fit(
X_train_smote,y_train_smote,
batch_size=batch_size,
epochs=epochs,
verbose=1,
validation_data = (X_val,y_val)
)
# Get end time
end=time.time()
# Time taken
print("Time taken in seconds ",end-start)
model_4.evaluate(X_train_smote,y_train_smote)
model_4.evaluate(X_val,y_val)
Loss function
# Plotting Train Loss vs Validation Loss
plot(history_4, 'loss')
# Plotting Train recall vs Validation recall
plot(history_4, 'recall')
y_train_pred = model_4.predict(X_train_smote)
#Predicting the results using 0.5 as the threshold
y_train_pred = (y_train_pred > 0.5)
y_train_pred
y_val_pred = model_4.predict(X_val)
#Predicting the results using 0.5 as the threshold
y_val_pred = (y_val_pred > 0.5)
y_val_pred
model_name = "NN with SMOTE & Adam - 3 Hidden Layers [32,32,32]"
train_metric_df.loc[model_name] = recall_score(y_train_smote,y_train_pred)
valid_metric_df.loc[model_name] = recall_score(y_val,y_val_pred)
results.loc[model_name]=['3','32,32,32','relu,relu,relu',epochs,batch_size,'Adam',(end-start),history_4.history["loss"][-1],history_4.history["val_loss"][-1],history_4.history["recall"][-1],history_3.history["val_recall"][-1]]
results
Classification report
cr=classification_report(y_train_smote,y_train_pred)
print(cr)
cr=classification_report(y_val, y_val_pred)
print(cr)
Confusion matrix
#Calculating the confusion matrix
make_confusion_matrix(y_train_smote, y_train_pred)
#Calculating the confusion matrix
make_confusion_matrix(y_val,y_val_pred)
# Clear the session memory
backend.clear_session()
#Fixing the seed for random number generators so that we can ensure we receive the same output everytime
np.random.seed(2)
random.seed(2)
tf.random.set_seed(2)
# Initializing the model
model_5 = Sequential()
# Add required # of neurons to the input layer with relu as activation function
model_5.add(Dense(32,activation='relu',input_dim = X_train_smote.shape[1]))
#model_5.add(BatchNormalization())
# Add dropout rate
model_5.add(Dropout(0.2))
# Add required # neurons to the hidden layer with any activation function.
model_5.add(Dense(32,activation='relu'))
#model_5.add(BatchNormalization())
# Add dropout rate.
model_5.add(Dropout(0.3))
# Add hidden layer with neurons with relu as activation function
model_5.add(Dense(32,activation='relu'))
# Add the required number of neurons in the output layer with a suitable activation function.
model_5.add(Dense(1, activation = 'sigmoid'))
# Complete the code to use Adam as the optimizer.
optimizer = tf.keras.optimizers.Adam(learning_rate=0.0001)
# uncomment one of the following lines to define the metric to be used
# metric = 'accuracy'
metric = keras.metrics.Recall()
# metric = keras.metrics.Precision()
# metric = keras.metrics.F1Score()
# Complete the code to compile the model with binary cross entropy as loss function and recall as the metric
model_5.compile(loss='binary_crossentropy',optimizer=optimizer,metrics=[metric])
model_5.summary()
epochs = 25
batch_size = 128
# Get start time
start = time.time()
history_5 = model_5.fit(
X_train_smote,y_train_smote,
batch_size=batch_size,
epochs=epochs,
verbose=1,
validation_data = (X_val,y_val))
# Get end time
end=time.time()
# Time taken
print("Time taken in seconds ",end-start)
model_5.evaluate(X_train_smote,y_train_smote)
model_5.evaluate(X_val,y_val)
Loss function
# Plotting Train Loss vs Validation Loss
plot(history_5, 'loss')
# Plotting Train recall vs Validation recall
plot(history_5, 'recall')
y_train_pred = model_5.predict(X_train_smote)
#Predicting the results using 0.5 as the threshold
y_train_pred = (y_train_pred > 0.5)
y_train_pred
y_val_pred = model_5.predict(X_val)
#Predicting the results using 0.5 as the threshold
y_val_pred = (y_val_pred > 0.5)
y_val_pred
model_name = "NN with SMOTE,Adam & Dropout - 3 Hidden Layers [32,32,32]"
train_metric_df.loc[model_name] = recall_score(y_train_smote,y_train_pred)
valid_metric_df.loc[model_name] = recall_score(y_val,y_val_pred)
results.loc[model_name]=['3','32,32,32','relu,relu,relu',epochs,batch_size,'Adam',(end-start),history_5.history["loss"][-1],history_5.history["val_loss"][-1],history_5.history["recall"][-1],history_5.history["val_recall"][-1]]
results
Classification report
cr=classification_report(y_train_smote,y_train_pred)
print(cr)
#classification report
cr=classification_report(y_val, y_val_pred) ## Complete the code to check the model's performance on the validation set
print(cr)
Confusion matrix
#Calculating the confusion matrix
make_confusion_matrix(y_train_smote, y_train_pred)
#Calculating the confusion matrix
make_confusion_matrix(y_val,y_val_pred) ## Complete the code to check the model's performance on the validation set
results
results.Train_loss - results.Valid_loss
y_test_pred = model_01.predict(X_test)
y_test_pred = (y_test_pred > 0.5)
print(y_test_pred)
#lets print classification report
cr=classification_report(y_test,y_test_pred)
print(cr)
#Calculating the confusion matrix
make_confusion_matrix(y_test,y_test_pred)
Our neural network model has a Recall score of .95, so it should do well with predicting customers that will indeed exit.
Our analyses revealed that:
Power Ahead